
#include <string.h>
#include "ev_packet.h"
#include "ev_utils.h"

int ev_parser_init(ev_parser_t *parser,
                   void *arg,
                   void (*cb)(const ev_parser_t *, const ev_packet_t *)
)
{
    memset(parser, 0, sizeof(ev_parser_t));
    parser->arg = arg;
    parser->cb = cb;
    parser->crc = EV_PACKET_MAGIC;
    return 0;
}

int ev_parser_reset(ev_parser_t *parser)
{
    parser->offset = 0;
    parser->crc = EV_PACKET_MAGIC;
    return 0;
}

static int _check_tail(const uint8_t *data, uint32_t len)
{
    if (len < EV_PACKET_TAIL_LEN) {
        return -3;
    }
    if (data[len - 10] != EV_PACKET_SOF) {
        return -4;
    }
    uint32_t cal_len = data[len - 4];
    cal_len <<= 8;
    cal_len += data[len - 5];
    if (cal_len != len - EV_PACKET_TAIL_LEN) {
        return -5;
    }

    uint8_t sum = 0;
    for (uint32_t i = len - EV_PACKET_TAIL_LEN; i < len - 2; i ++) {
        sum += data[i];
    }
    if (sum != 0) {
        return -6;
    }

    return 0;
}

int ev_parser_feed_one(ev_parser_t *parser, uint8_t data)
{
    if (parser->offset >= EV_PACKET_MAX_LEN) {
        ev_parser_reset(parser);
    }
    parser->buf[parser->offset ++] = data;
    parser->crc = ev_crc16(&data, 1, parser->crc);
    if (!parser->crc && _check_tail(parser->buf, parser->offset) == 0) {
        if (parser->cb && parser->offset >= EV_PACKET_TAIL_LEN) {
            ev_packet_t packet;
            packet.user = parser->buf[parser->offset - 9];
            packet.seq = parser->buf[parser->offset - 8];
            packet.cmd = parser->buf[parser->offset - 6];  // cmd high byte
            packet.cmd <<= 8;
            packet.cmd += parser->buf[parser->offset - 7]; // cmd low byte
            packet.len = parser->offset - EV_PACKET_TAIL_LEN;
            memcpy(packet.data, parser->buf, packet.len);
            parser->cb(parser, &packet);
        }
        ev_parser_reset(parser);
    }
    return 0;
}

int ev_parser_feed(ev_parser_t *parser, const uint8_t *data, uint32_t len)
{
    for (uint32_t i = 0; i < len; i++) {
        ev_parser_feed_one(parser, data[i]);
    }
    return 0;
}

int ev_packet_make(const ev_packet_t *pkt, uint8_t *data, uint32_t *len)
{
    uint32_t cal_len = 0;
    uint8_t chksum = 0;
    if (pkt && data && len && pkt->len <= EV_PACKET_MAX_DATA_LEN) {
        memcpy(data, pkt->data, pkt->len);
        cal_len += pkt->len;

        data[cal_len ++] = EV_PACKET_SOF;  // sof
        data[cal_len ++] = pkt->user;      // user
        data[cal_len ++] = pkt->seq;       // seq
        data[cal_len ++] = (pkt->cmd & 0xff); // cmd low byte
        data[cal_len ++] = (pkt->cmd >> 8) & 0xff; // cmd high byte
        data[cal_len ++] = (pkt->len & 0xff); // len low byte
        data[cal_len ++] = (pkt->len >> 8) & 0xff; // len high byte

        for (uint32_t i = cal_len - 7; i < cal_len; i ++) {
            chksum += data[i];
        }
        data[cal_len ++] = (~chksum) + 1;   // chksum

        uint32_t crc = ev_crc16(data, cal_len, EV_PACKET_MAGIC);
        data[cal_len ++] = crc & 0xff;
        data[cal_len ++] = (crc >> 8) & 0xff;
        *len = cal_len;
        return 0;
    }

    return -1;
}

int ev_packet_check(const uint8_t *data, uint32_t len, ev_packet_t *pkt)
{
    if (!data || len < EV_PACKET_TAIL_LEN || len > EV_PACKET_MAX_LEN) {
        return -1;
    }
    if (ev_crc16(data, len, EV_PACKET_MAGIC)) {
        return -2;
    }
    int ret = _check_tail(data, len);
    if (ret) {
        return ret;
    }
    if (pkt) {
        pkt->user = data[len - 9];
        pkt->seq = data[len - 8];
        pkt->cmd = data[len - 6];  // cmd high byte
        pkt->cmd <<= 8;
        pkt->cmd += data[len - 7]; // cmd low byte
        pkt->len = len - EV_PACKET_TAIL_LEN;
        memcpy(pkt->data, data, pkt->len);
    }
    return 0;
}
